[Android] 利用surfaceview制作自定义组件

surfaceView系列00

Posted by Aerber Zhou on 2017-03-18

一.SurfaceView和View最本质的区别

surfaceview继承自view,但是同view由本质上的区别,即是surfaceview开一个新线程进行绘制,view是在UI主线程中进行绘制

SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。可以在主线程之外的线程中向屏幕绘图。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。

那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。

当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

所以基于以上,根据游戏特点,一般分成两类。

(1) 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

(2) 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

二.需要重写的方法

 (1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}

     //在surface的大小发生改变时激发

 (2)public void surfaceCreated(SurfaceHolder holder){}

     //在创建时激发,一般在这里调用画图的线程。

 (3)public void surfaceDestroyed(SurfaceHolder holder) {}

     //销毁时激发,一般在这里将画图的线程停止、释放。

原因:

使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始,changed和created都是创建的过程.这样可以被直接复制到显存从而显示出来,这使得显示速度会非常快.
而在surface被销毁之前surfaceview必须被销毁
故Callback 中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。

整个自定义surfaceview的绘制过程:

继承surfaceview
通过surface.getholder()获得当前画面的surfaceholder对象
添加回调函数 surfaceholder.addcallback(callback),并实现surfaceholder.callback接口,即重载 surfaceChanged surfaceCreated surfaceDestroyed三个函数
    在surfaceCreated中启动一个新线程,在线程中进行draw()操作
    在surfaceDestroyed中把那个线程终止并把线程置空
通过surfaceholder.lockcanvas()返回canvas对象,锁定画布
canvas画图
surfaceholder.unlockcanvas()解锁画布,提交改变,显示画图

三.surfaceholder是什么

相当于是surface的控制器,用于操控surface,处理canvas上面的对象

几个需要注意的方法:

(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。

四.构造函数中的 attributeset 属性

attributeset属性相当于给这个控件一个属性

这个属性在构造函数中使用默认样式,在xml文件中可以自定义# 利用surfaceview制作自定义组件